Skip to content

Fix SSR failure: make useDataScope context-optional#295

Merged
hotlong merged 15 commits intomainfrom
copilot/fix-action-run-issues
Jan 31, 2026
Merged

Fix SSR failure: make useDataScope context-optional#295
hotlong merged 15 commits intomainfrom
copilot/fix-action-run-issues

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 31, 2026

Next.js build failing on /docs/components/complex/table during static generation because useDataScope unconditionally throws when SchemaRendererProvider is absent.

Changes

  • useDataScope hook: Return undefined instead of throwing when context is missing. Added guard to prevent reduce operation on undefined dataSource.

  • SimpleTableRenderer: Support inline data fallback (schema.data, schema.props.data) when no binding exists. Accept both accessorKey/key and header/label column properties for API flexibility.

// Before: throws during SSR
const data = useDataScope(schema.bind);

// After: gracefully handles missing context
const boundData = useDataScope(schema.bind);
const data = boundData || schema.data || schema.props?.data || [];

This preserves data binding functionality while enabling components to render during static generation with inline data.

Original prompt

引用: https://github.com/objectstack-ai/objectui/actions/runs/21539734678/job/62072222406#step:8:1


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@vercel
Copy link
Copy Markdown

vercel bot commented Jan 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectui Ready Ready Preview, Comment Jan 31, 2026 8:47am
objectui-components Ready Ready Preview, Comment Jan 31, 2026 8:47am
objectui-crm-app Ready Ready Preview, Comment Jan 31, 2026 8:47am

Request Review

Copilot AI and others added 2 commits January 31, 2026 06:01
- Modified useDataScope to handle missing SchemaRendererProvider gracefully
- Updated table renderer to support both inline data and data binding
- Added support for both accessorKey and key column properties
- Ensured compatibility with Next.js static generation

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
- Add guard in useDataScope to return early if dataSource is undefined
- Add validation in table renderer to handle missing accessor properties
- Ensure empty string is displayed instead of undefined values

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix issues with action run in ObjectUI Fix SSR failure: make useDataScope context-optional Jan 31, 2026
Copilot AI requested a review from hotlong January 31, 2026 06:07
@hotlong hotlong marked this pull request as ready for review January 31, 2026 06:11
Copilot AI review requested due to automatic review settings January 31, 2026 06:11
@github-actions
Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

Package Size Gzipped
components (index.js) 1761.89KB 416.55KB
core (index.js) 0.61KB 0.30KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 16.91KB 4.46KB
fields (index.js) 85.45KB 16.69KB
layout (index.js) 12.63KB 3.91KB
plugin-aggrid (AgGridImpl-BQ6tBvrq.js) 5.27KB 1.92KB
plugin-aggrid (ObjectAgGridImpl-CGFeGvOH.js) 11.44KB 3.52KB
plugin-aggrid (index-CLKYMco3.js) 19.20KB 4.89KB
plugin-aggrid (index.js) 0.22KB 0.16KB
plugin-calendar (index.js) 33.19KB 8.30KB
plugin-charts (AdvancedChartImpl-Cyae7QYH.js) 69.51KB 16.23KB
plugin-charts (BarChart-RKJxvg5Y.js) 535.74KB 134.11KB
plugin-charts (ChartImpl-CrYQhLTB.js) 8.78KB 3.11KB
plugin-charts (index-BGwsUQF8.js) 12.68KB 3.91KB
plugin-charts (index.js) 0.21KB 0.16KB
plugin-chatbot (index.js) 18.39KB 5.22KB
plugin-dashboard (index.js) 11.99KB 3.82KB
plugin-editor (MonacoImpl-hfdmoz6k.js) 18.15KB 5.59KB
plugin-editor (index-CuYbY6xb.js) 10.10KB 3.32KB
plugin-editor (index.js) 0.19KB 0.15KB
plugin-form (index.js) 14.46KB 4.65KB
plugin-gantt (index.js) 18.03KB 5.27KB
plugin-grid (index.js) 40.53KB 11.23KB
plugin-kanban (KanbanImpl-kbzXvR7a.js) 76.50KB 20.46KB
plugin-kanban (index-4iNpG2-h.js) 11.89KB 3.68KB
plugin-kanban (index.js) 0.18KB 0.15KB
plugin-map (index.js) 16.78KB 5.12KB
plugin-markdown (MarkdownImpl-9F_xYhwR.js) 256.79KB 64.50KB
plugin-markdown (index-DZS4FNIJ.js) 9.63KB 3.17KB
plugin-markdown (index.js) 0.19KB 0.15KB
plugin-timeline (index.js) 23.93KB 5.96KB
plugin-view (index.js) 16.69KB 4.93KB
react (LazyPluginLoader.js) 1.10KB 0.58KB
react (SchemaRenderer.js) 3.02KB 1.21KB
react (index.js) 0.39KB 0.25KB
react (index.test.js) 0.34KB 0.26KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 0.20KB 0.18KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (disclosure.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (index.js) 0.34KB 0.25KB
types (layout.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (theme.js) 0.20KB 0.18KB
types (views.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a Next.js SSR build failure by making the useDataScope hook context-optional, allowing components to render during static generation without throwing errors when SchemaRendererProvider is absent.

Changes:

  • Modified useDataScope to return undefined instead of throwing when context is missing
  • Enhanced SimpleTableRenderer to support inline data fallback and flexible column property names for backwards compatibility

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.

File Description
packages/react/src/context/SchemaRendererContext.tsx Made useDataScope context-optional by using useContext directly and returning undefined when context is missing
packages/components/src/renderers/data-display/table.tsx Added inline data fallback chain and support for both accessorKey/key and header/label column properties

Comment on lines +23 to +26
// Try to get data from binding first, then fall back to inline data
const boundData = useDataScope(schema.bind);
const data = boundData || schema.data || schema.props?.data || [];
const columns = schema.columns || schema.props?.columns || [];
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new inline data fallback behavior lacks test coverage. The repository has comprehensive automated testing for other components (see packages/components/src/__tests__/layout-data-renderers.test.tsx), but there are no tests for the SimpleTableRenderer or the new data fallback chain.

Tests should verify:

  1. Data binding via useDataScope when context is available
  2. Fallback to schema.data when no binding exists
  3. Fallback to schema.props?.data as a third option
  4. Graceful handling when useDataScope returns undefined (SSR scenario)
  5. The new column property aliases (key/accessorKey, label/header)

This is especially important for SSR scenarios which are harder to debug in production.

Copilot uses AI. Check for mistakes.
Comment on lines 37 to 42
export const useDataScope = (path?: string) => {
const { dataSource } = useSchemaContext();
if (!path) return dataSource;
const context = useContext(SchemaRendererContext);
const dataSource = context?.dataSource;
if (!dataSource || !path) return dataSource;
// Simple path resolution for now. In real app might be more complex
return path.split('.').reduce((acc, part) => acc && acc[part], dataSource);
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The behavioral change to make useDataScope context-optional lacks test coverage. While the change fixes SSR issues, there are no tests verifying:

  1. The hook returns undefined when context is missing (new behavior)
  2. The hook still works correctly when context is available
  3. Path resolution works correctly with the optional context
  4. Edge case: what happens when context exists but dataSource is undefined

The repository has test infrastructure (packages/react/src/__tests__/) but no coverage for this critical hook. This is a significant behavioral change that should be tested to prevent regressions.

Copilot uses AI. Check for mistakes.
if (!path) return dataSource;
const context = useContext(SchemaRendererContext);
const dataSource = context?.dataSource;
if (!dataSource || !path) return dataSource;
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a potential edge case issue with the guard condition. When !dataSource is true (context is missing or dataSource is explicitly undefined/null/empty), the function returns dataSource which could be undefined. However, when !path is true but dataSource exists, it also returns dataSource.

This conflates two different scenarios:

  1. Context missing → should return undefined
  2. No path provided → should return the full dataSource

Consider separating these cases for clarity:

if (!dataSource) return undefined;
if (!path) return dataSource;

This makes the intent clearer and ensures consistent behavior.

Suggested change
if (!dataSource || !path) return dataSource;
if (!dataSource) return undefined;
if (!path) return dataSource;

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +44
{columns.map((col: any, index: number) => (
<TableHead key={col.key || col.accessorKey || index}>
{col.label || col.header}
</TableHead>
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to Rule #0 of the coding guidelines, all component schemas must strictly follow definitions in @objectstack/spec. The standard TableColumn interface (defined in packages/types/src/data-display.ts:203-260) uses header and accessorKey as the canonical property names, not label and key.

While adding fallback support for key/label provides backwards compatibility, this creates ambiguity and deviates from the spec. The documentation at content/docs/components/complex/table.mdx:14-20 and examples in packages/components/src/stories-json/data-table.stories.tsx:21-24 consistently use header and accessorKey.

Consider either:

  1. Removing the key/label aliases and only supporting the spec-compliant accessorKey/header properties, or
  2. Documenting this deviation from the spec with a clear migration path if these are legacy properties being phased out.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +58 to +66
{columns.map((col: any, index: number) => {
const accessor = col.key || col.accessorKey || '';
const value = accessor ? row[accessor] : '';
return (
<TableCell key={col.key || col.accessorKey || index}>
{value}
</TableCell>
);
})}
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to Rule #0 of the coding guidelines, all component schemas must strictly follow definitions in @objectstack/spec. The standard TableColumn interface uses accessorKey and header as the canonical property names, not key and label.

The fallback to col.key and col.accessorKey creates ambiguity. The code should consistently use the spec-compliant property names. Consider removing support for the non-standard key property or documenting why this deviation from the spec is necessary.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +23 to +25
// Try to get data from binding first, then fall back to inline data
const boundData = useDataScope(schema.bind);
const data = boundData || schema.data || schema.props?.data || [];
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to Rule #2 (Documentation Driven Development) of the coding guidelines, the task is not complete until the documentation reflects the new code/architecture.

This PR introduces significant behavioral changes:

  1. useDataScope now returns undefined instead of throwing when context is missing
  2. Table component supports inline data via schema.data or schema.props.data

These changes should be documented in:

  1. packages/react/README.md or equivalent documentation for the useDataScope hook behavior change
  2. content/docs/components/complex/table.mdx to show the inline data usage pattern for SSR scenarios

The documentation currently shows examples using only the data property directly on the schema, but doesn't explain the context-optional behavior or the fallback chain (boundData || schema.data || schema.props?.data).

Copilot generated this review using guidance from repository custom instructions.
// Try to get data from binding first, then fall back to inline data
const boundData = useDataScope(schema.bind);
const data = boundData || schema.data || schema.props?.data || [];
const columns = schema.columns || schema.props?.columns || [];
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The component supports multiple ways to specify columns (schema.columns vs schema.props?.columns), which creates ambiguity. According to the @objectstack/spec protocol, component properties should follow a consistent structure.

Looking at the ObjectGrid implementation (packages/plugin-grid/src/ObjectGrid.tsx:174), columns are accessed via schema.columns, not schema.props.columns. The SchemaRenderer passes schema properties both as direct props and under schema.props, but the standard pattern appears to be accessing them directly from the schema object.

Consider standardizing on schema.columns only, or document why both paths are necessary.

Suggested change
const columns = schema.columns || schema.props?.columns || [];
const columns = schema.columns ?? [];

Copilot uses AI. Check for mistakes.
Copilot AI and others added 6 commits January 31, 2026 06:17
- Add deprecation warning when registering components without namespace
- Fix Registry.get() to not fallback when namespace is explicitly provided
- Fix Registry.has() to not fallback when namespace is explicitly provided
- Fix Registry.getConfig() to not fallback when namespace is explicitly provided
- Fix PluginSystem tests to use legacy mode (useScope: false) for direct registry access
- All 91 tests now passing

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
- Add comment explaining backward compatibility behavior in Registry.register()
- Add comments to PluginSystem tests explaining legacy mode usage
- Clarify that last registration wins for non-namespaced lookups

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Add passWithNoTests: true to all vitest configurations to allow packages without test files to pass CI. This affects:
- Root vitest.config.mts
- All package-level vite.config.ts files with test sections

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

Package Size Gzipped
components (index.js) 1761.89KB 416.55KB
core (index.js) 0.61KB 0.30KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 16.91KB 4.46KB
fields (index.js) 85.45KB 16.69KB
layout (index.js) 12.63KB 3.91KB
plugin-aggrid (AgGridImpl-BQ6tBvrq.js) 5.27KB 1.92KB
plugin-aggrid (ObjectAgGridImpl-CGFeGvOH.js) 11.44KB 3.52KB
plugin-aggrid (index-CLKYMco3.js) 19.20KB 4.89KB
plugin-aggrid (index.js) 0.22KB 0.16KB
plugin-calendar (index.js) 33.19KB 8.30KB
plugin-charts (AdvancedChartImpl-Cyae7QYH.js) 69.51KB 16.23KB
plugin-charts (BarChart-RKJxvg5Y.js) 535.74KB 134.11KB
plugin-charts (ChartImpl-CrYQhLTB.js) 8.78KB 3.11KB
plugin-charts (index-BGwsUQF8.js) 12.68KB 3.91KB
plugin-charts (index.js) 0.21KB 0.16KB
plugin-chatbot (index.js) 18.39KB 5.22KB
plugin-dashboard (index.js) 11.99KB 3.82KB
plugin-editor (MonacoImpl-hfdmoz6k.js) 18.15KB 5.59KB
plugin-editor (index-CuYbY6xb.js) 10.10KB 3.32KB
plugin-editor (index.js) 0.19KB 0.15KB
plugin-form (index.js) 14.46KB 4.65KB
plugin-gantt (index.js) 18.03KB 5.27KB
plugin-grid (index.js) 40.53KB 11.23KB
plugin-kanban (KanbanImpl-kbzXvR7a.js) 76.50KB 20.46KB
plugin-kanban (index-4iNpG2-h.js) 11.89KB 3.68KB
plugin-kanban (index.js) 0.18KB 0.15KB
plugin-map (index.js) 16.78KB 5.12KB
plugin-markdown (MarkdownImpl-9F_xYhwR.js) 256.79KB 64.50KB
plugin-markdown (index-DZS4FNIJ.js) 9.63KB 3.17KB
plugin-markdown (index.js) 0.19KB 0.15KB
plugin-timeline (index.js) 23.93KB 5.96KB
plugin-view (index.js) 16.69KB 4.93KB
react (LazyPluginLoader.js) 1.10KB 0.58KB
react (SchemaRenderer.js) 3.02KB 1.21KB
react (index.js) 0.39KB 0.25KB
react (index.test.js) 0.34KB 0.26KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 0.20KB 0.18KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (disclosure.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (index.js) 0.34KB 0.25KB
types (layout.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (theme.js) 0.20KB 0.18KB
types (views.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@github-actions
Copy link
Copy Markdown
Contributor

✅ All checks passed!

  • ✅ Type check passed
  • ✅ Tests passed
  • ✅ Lint check completed

Copilot AI and others added 5 commits January 31, 2026 07:38
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
@github-actions github-actions bot added the documentation Improvements or additions to documentation label Jan 31, 2026
@github-actions
Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

Package Size Gzipped
components (index.js) 1761.89KB 416.55KB
core (index.js) 0.61KB 0.30KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 16.91KB 4.46KB
fields (index.js) 85.45KB 16.69KB
layout (index.js) 12.63KB 3.91KB
plugin-aggrid (AgGridImpl-BQ6tBvrq.js) 5.27KB 1.92KB
plugin-aggrid (ObjectAgGridImpl-CGFeGvOH.js) 11.44KB 3.52KB
plugin-aggrid (index-CLKYMco3.js) 19.20KB 4.89KB
plugin-aggrid (index.js) 0.22KB 0.16KB
plugin-calendar (index.js) 33.19KB 8.30KB
plugin-charts (AdvancedChartImpl-Cyae7QYH.js) 69.51KB 16.23KB
plugin-charts (BarChart-RKJxvg5Y.js) 535.74KB 134.11KB
plugin-charts (ChartImpl-CrYQhLTB.js) 8.78KB 3.11KB
plugin-charts (index-BGwsUQF8.js) 12.68KB 3.91KB
plugin-charts (index.js) 0.21KB 0.16KB
plugin-chatbot (index.js) 18.39KB 5.22KB
plugin-dashboard (index.js) 11.99KB 3.82KB
plugin-editor (MonacoImpl-hfdmoz6k.js) 18.15KB 5.59KB
plugin-editor (index-CuYbY6xb.js) 10.10KB 3.32KB
plugin-editor (index.js) 0.19KB 0.15KB
plugin-form (index.js) 14.46KB 4.65KB
plugin-gantt (index.js) 18.03KB 5.27KB
plugin-grid (index.js) 40.53KB 11.23KB
plugin-kanban (KanbanImpl-kbzXvR7a.js) 76.50KB 20.46KB
plugin-kanban (index-4iNpG2-h.js) 11.89KB 3.68KB
plugin-kanban (index.js) 0.18KB 0.15KB
plugin-map (index.js) 16.78KB 5.12KB
plugin-markdown (MarkdownImpl-9F_xYhwR.js) 256.79KB 64.50KB
plugin-markdown (index-DZS4FNIJ.js) 9.63KB 3.17KB
plugin-markdown (index.js) 0.19KB 0.15KB
plugin-timeline (index.js) 23.93KB 5.96KB
plugin-view (index.js) 16.69KB 4.93KB
react (LazyPluginLoader.js) 1.10KB 0.58KB
react (SchemaRenderer.js) 3.02KB 1.21KB
react (index.js) 0.39KB 0.25KB
react (index.test.js) 0.34KB 0.26KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 0.20KB 0.18KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (disclosure.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (index.js) 0.34KB 0.25KB
types (layout.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (theme.js) 0.20KB 0.18KB
types (views.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@github-actions
Copy link
Copy Markdown
Contributor

✅ All checks passed!

  • ✅ Type check passed
  • ✅ Tests passed
  • ✅ Lint check completed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants